/*
	BrushFile.m
        Copyright 2008 Laura Dickey.  All rights reserved.

        This file is part of BrushViewQL.

        BrushViewQL is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 2 of the License, or
        (at your option) any later version.

        BrushViewQL is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with BrushViewQL; if not, write to the Free Software
        Foundation, Inc., 59 Temple Place, Suite 330,
        Boston, MA  02111-1307  USA

        You may contact the author at: brushviewql@gmail.com
*/

#import "BrushFile.h"
#import "DataSegment.h"
#import "Abr12Brush.h"
#import <Quartz/Quartz.h>

@implementation BrushFile

#define READ_SHORT(val) \
	val = *(unsigned short *)brushData;\
	val = CFSwapInt16BigToHost(val);\
	brushData += sizeof(val);

+ (id)createImage:(unsigned char **)brushDataPtr 
			width:(long)w height:(long)h depth:(int)d compressed:(bool)compress
{
	
	NSBitmapImageRep	*newBitmap;
	CIImage			*theImage;
	int			*lineLengths;
	unsigned char		*bitmapData, *startBitmapData, *brushData;
	long			size;
	int			c, t, i, j, n, p, tw;
	unsigned short		len;

	brushData = *brushDataPtr;
	size = w * h * (d >> 3);
	//NSLog(@"%lld %lld %d", w,h,d);
	
	newBitmap = [[NSBitmapImageRep alloc]
		initWithBitmapDataPlanes:0 
		pixelsWide:w pixelsHigh:h bitsPerSample:d
		samplesPerPixel:1 hasAlpha:NO isPlanar:NO
		colorSpaceName:NSDeviceBlackColorSpace
		bytesPerRow:w bitsPerPixel:0];
	[newBitmap retain];
	bitmapData = [newBitmap bitmapData];
	startBitmapData = bitmapData;
	t = 0;
	if (compress) {
		// read compressed data
		lineLengths = malloc(h * sizeof(*lineLengths));
		for (i = 0; i < h; i++) {
			 READ_SHORT(len);
			 lineLengths[i] = len;
			 //BRLog(@"line[%d] = %d", i, lineLengths[i]);
		}
		for (i = 0; i < h; i++) {
			tw = 0;
			//BRLog(@"i = %d", i);
			for (j = 0; j < lineLengths[i];) {
				//BRLog(@"j = %d", j);
				//BRLog(@"delta = %d", bitmapData-startBitmapData);
				n = *(char *)brushData++;
				++j;
				// force p to be a signed number
				if (n >= 128) {
					n -= 256;
				}
				if (n < 0) {
					if (n == -128) {
						continue; // nop
					}
					n = -n+1;
					p = *(char *)brushData++;
					j++;
					for (c = 0; c < n; c++) {
						//BRLog(@"c = %d, n = %d", c, n);
						*bitmapData++ = p;
						++t;
						++tw;
					}
					
				} else {
					for (c = 0; c < n+1; c++, j++) {
						*bitmapData++ =
							*(char *)brushData++;
						++t;
						++tw;
					}
				}
			}
			if ((bitmapData - startBitmapData) > size) {
			//	BRLog(@"something funky is up with bitmapData");
			}
			if (tw != w) {
			//	BRLog(@"tw(%d) != width(%d)",
					//tw, w);
			}
		}
		if (lineLengths) {
			free(lineLengths);
		}
	} else {
		//BRLog(@"Brush not compressed!");
		memcpy(bitmapData, brushData, size);
	}
	
	//NSLog(@"create transparency...");
	
	//
	// My method to get to output with transparency
	//
	theImage = [[CIImage alloc] initWithData:[newBitmap TIFFRepresentation]];
		
	//inverting the image
	CIFilter *invert = [CIFilter filterWithName:@"CIColorInvert"];
	[invert setValue:theImage forKey:@"inputImage"];
	theImage = [invert valueForKey:@"outputImage"];
	
	//and set the mask
	CIFilter *masktoalpha = [CIFilter filterWithName:@"CIMaskToAlpha"];
	[masktoalpha setValue:theImage forKey:@"inputImage"];
	theImage = [masktoalpha valueForKey:@"outputImage"];
	
	
	*brushDataPtr = brushData;
	
	return theImage;
}

	

- (void)loadVersion6Brushes:(NSFileHandle *)fHandle vers:(int)subvers
{
	unsigned long long	initialOffset;
	NSData	*tmpData;
	DataSegment	*segment;
	unsigned long	length;
	NSString	*tagname;
	
	sections = [NSMutableDictionary	dictionaryWithCapacity:5];
	[sections retain];
	initialOffset = [fHandle offsetInFile];
	
	tmpData = [fHandle readDataOfLength:4];
	while ([tmpData bytes]) {
		segment = [[DataSegment alloc] init];
		[segment setSubVersion:subvers];
		[segment setTag:tmpData];
		tmpData = [fHandle readDataOfLength:4];
		[segment setTagName:tmpData];
		tagname = [[NSString alloc]
			 initWithBytes:[tmpData bytes] length:4 
			 encoding: NSASCIIStringEncoding];
		tmpData = [fHandle readDataOfLength:4];
		length = *(unsigned long *)[tmpData bytes];
		length = CFSwapInt32BigToHost(length);
		[segment setLength:length];
		tmpData = [fHandle readDataOfLength:length];
		[segment setBytes:tmpData];
		[sections setObject:segment forKey:tagname];
		//BRLog(@"read in segment %@ - %d bytes", tagname, length);
		tmpData = [fHandle readDataOfLength:4];
	}
	//BRLog(@"Done parsing file");
	// now look at the brush
	segment = [sections objectForKey:@"samp"];
	myImages = [segment loadBrushes];
//	BRLog(@"got back array of %d brushes", [myImages count]);
//	BRLog(@"called setNeedsDisplay on imageView");
}

- (void)loadVersion12Brushes:(NSFileHandle *)fHandle count:(int)numBrushes
{
	NSData		*tmpData;
	Abr12Brush	*aBrush;
	NSImage		*theImage;
	unsigned char	*brushData;
	int		i;
	long		size;
	short		type;
	
	myImages = [[NSMutableArray arrayWithCapacity:numBrushes] retain];
	myVers12Brushes = 
		[[NSMutableArray arrayWithCapacity:numBrushes] retain];
	for (i = 0; i < numBrushes; i++) {
		tmpData = [fHandle readDataOfLength:2];
		type = *(short *)[tmpData bytes];
		type = CFSwapInt16BigToHost(type);
		tmpData = [fHandle readDataOfLength:4];
		size = *(long *)[tmpData bytes];
		size = CFSwapInt32BigToHost(size);
		
		if (type != 2) {
			/* skip anything other than sampled brushes */
			[fHandle seekToFileOffset:
				([fHandle offsetInFile] + size)];
			continue;
		}
		tmpData = [fHandle readDataOfLength:size];
		aBrush = [[Abr12Brush alloc] init];
		brushData = (unsigned char *)[tmpData bytes];
		theImage = [aBrush loadData:brushData 
				version:version];
		[myImages insertObject:theImage atIndex:0];
	}
}

- (bool)parseBrushFile:(NSString *)thePath
{
	NSFileHandle	*fHandle;
	unsigned short	count, subversion;
	NSData		*tmpData;
	
	filePath = [thePath copy];
	fHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
	NSAssert(fHandle != nil, @"nil fHandle from fileHandleForReadingAtPath");

	tmpData = [fHandle readDataOfLength:sizeof(version)];
	version = *(unsigned short *)[tmpData bytes];
	version = CFSwapInt16BigToHost(version);
	tmpData = [fHandle readDataOfLength:sizeof(count)];
	count = *(unsigned short *)[tmpData bytes];
	count = CFSwapInt16BigToHost(count);
	
	if (version == 6) {
		subversion = count;
		if ((subversion != 1) && (subversion != 2)) {
			NSAssert(0, @"Invalid subversion for version 6");
		}
		[self loadVersion6Brushes:fHandle vers:subversion];
	} else if ((version != 1) && (version != 2)) {
		NSAssert1(0, @"Invalid version %d", version);
	} else {
		[self loadVersion12Brushes:fHandle count:count];

	}
	return YES;
}


- (NSArray *)getImagesWithWidth:(double)thewidth andHeight:(double)theheight;
{
	
	
	//if needs fitting with width
	if (thewidth != 0.0) {
	 
		for (int i=0;i<[myImages count];i++) {
			
			double originalwidth = [[myImages objectAtIndex:i] extent].size.width;
			
			double scale =thewidth/originalwidth;
			
			CIImage* scaledImage = [myImages objectAtIndex:i];
			
			CIFilter *filter = [CIFilter filterWithName:
								@"CILanczosScaleTransform"];
			[filter setValue:[NSNumber numberWithFloat:scale]
					  forKey:@"inputScale"];
			[filter setValue:[NSNumber numberWithFloat:1.0]
					  forKey:@"inputAspectRatio"];
			[filter setValue:scaledImage forKey:@"inputImage"];
			scaledImage = [filter valueForKey:@"outputImage"];
			
			[myImages replaceObjectAtIndex:i withObject:scaledImage];
		}
		
	 	
	}


	//if needs fitting with height
	if (theheight != 0.0) {
		
		for (int i=0;i<[myImages count];i++) {
			
			double originalheight = [[myImages objectAtIndex:i] extent].size.height;
			
			double scale = theheight/originalheight;
			
			CIImage* scaledImage = [myImages objectAtIndex:i];
			
			CIFilter *filter = [CIFilter filterWithName:
								@"CILanczosScaleTransform"];
			[filter setValue:[NSNumber numberWithFloat:scale]
					  forKey:@"inputScale"];
			[filter setValue:[NSNumber numberWithFloat:1.0]
					  forKey:@"inputAspectRatio"];
			[filter setValue:scaledImage forKey:@"inputImage"];
			scaledImage = [filter valueForKey:@"outputImage"];
			
			[myImages replaceObjectAtIndex:i withObject:scaledImage];
		}
		
	}
	
	
	return myImages;
}


- (id)getPath
{
	return filePath;
}

@end
